/**
 * www.easyplatform.cn ©2016
 */
package cn.easyplatform.studio.web.editors.entity;

import cn.easyplatform.entities.beans.bpm.BpmBean;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.studio.StudioException;
import cn.easyplatform.studio.context.Contexts;
import cn.easyplatform.studio.vos.BizQueryVo;
import cn.easyplatform.studio.vos.FieldVo;
import cn.easyplatform.studio.web.editors.AbstractEntityEditor;
import cn.easyplatform.studio.web.editors.BpmEditorCallback;
import cn.easyplatform.studio.web.layout.WorkbenchController;
import cn.easyplatform.studio.web.views.impl.AbstractView;
import cn.easyplatform.type.EntityType;
import cn.easyplatform.web.ext.bpm.Bpmdesigner;
import cn.easyplatform.web.ext.cmez.CMeditor;
import org.snaker.engine.Context;
import org.snaker.engine.helper.SnakerHelper;
import org.snaker.engine.helper.XmlHelper;
import org.snaker.engine.impl.SimpleContext;
import org.snaker.engine.model.NodeModel;
import org.snaker.engine.model.ProcessModel;
import org.snaker.engine.model.TransitionModel;
import org.snaker.engine.parser.NodeParser;
import org.snaker.engine.parser.impl.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Idspace;
import org.zkoss.zul.Tabpanel;

import javax.xml.parsers.DocumentBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * @author <a href="mailto:shiny_vc@163.com">陈云亮</a> <br/>
 * @since 2.0.0 <br/>
 */
public class BpmEntityEditor extends AbstractEntityEditor<BpmBean> {

	private Bpmdesigner designer;

	/**
	 * @param workbench
	 * @param entity
	 * @param code
	 */
	public BpmEntityEditor(WorkbenchController workbench, BpmBean entity,
			char code) {
		super(workbench, entity, code);
	}

	@Override
	public void create(Object... args) {
		super.create(args);
		Tabpanel tabpanel = new Tabpanel();
		Idspace is = new Idspace();
		is.setHflex("1");
		is.setVflex("1");
		is.setParent(tabpanel);
		Executions.createComponents("~./include/editor/entity/bpm.zul", is,
				null);
		for (Component comp : is.getFellows()) {
			if (comp.getId().equals("bpm_cmeditor_source")) {
				source = (CMeditor) comp;
				source.setTheme(Contexts.getUser().getEditorTheme());
				source.setValue(entity.getContent());
			} else if (comp.getId().equals("bpm_bpmdesigner_designer")) {
				designer = (Bpmdesigner) comp;
				if (Strings.isBlank(entity.getContent())) {
					designer.setModel("{}");
				} else {
					try {
						ProcessModel model = parse(entity.getContent()
								.getBytes("utf-8"));
						designer.setModel(SnakerHelper.getModelJson(model));
					} catch (UnsupportedEncodingException e) {
					}
				}
				designer.setTask("{'activeRects':{'rects':[{'paths':[],'name':''}]}}");
				designer.addEventListener(Events.ON_OPEN, this);
				designer.addEventListener(Bpmdesigner.ON_SAVE, this);
			}
		}
		workbench.addEditor(tab, tabpanel);
	}

	private ProcessModel parse(byte[] data) {
		DocumentBuilder documentBuilder = XmlHelper.createDocumentBuilder();
		if (documentBuilder != null) {
			Document doc = null;
			try {
				doc = documentBuilder.parse(new ByteArrayInputStream(data));
				Element processE = doc.getDocumentElement();
				ProcessModel process = new ProcessModel();
				process.setName(processE.getAttribute(NodeParser.ATTR_NAME));
				process.setDisplayName(processE
						.getAttribute(NodeParser.ATTR_DISPLAYNAME));
				process.setExpireTime(processE
						.getAttribute(NodeParser.ATTR_EXPIRETIME));
				process.setInstanceUrl(processE
						.getAttribute(NodeParser.ATTR_INSTANCEURL));
				process.setInstanceNoClass(processE
						.getAttribute(NodeParser.ATTR_INSTANCENOCLASS));
				NodeList nodeList = processE.getChildNodes();
				int nodeSize = nodeList.getLength();
				for (int i = 0; i < nodeSize; i++) {
					Node node = nodeList.item(i);
					if (node.getNodeType() == Node.ELEMENT_NODE) {
						NodeModel model = parseModel(node);
						process.getNodes().add(model);
					}
				}

				// 循环节点模型，构造变迁输入、输出的source、target
				for (NodeModel node : process.getNodes()) {
					for (TransitionModel transition : node.getOutputs()) {
						String to = transition.getTo();
						for (NodeModel node2 : process.getNodes()) {
							if (to.equalsIgnoreCase(node2.getName())) {
								node2.getInputs().add(transition);
								transition.setTarget(node2);
							}
						}
					}
				}
				return process;
			} catch (SAXException e) {
				throw new StudioException(e);
			} catch (IOException e) {
				throw new StudioException(e);
			}
		} else {
			throw new StudioException("documentBuilder is null");
		}
	}

	private static NodeModel parseModel(Node node) {
		String nodeName = node.getNodeName();
		Element element = (Element) node;
		NodeParser nodeParser = null;
		try {
			Context ctx = new SimpleContext();
			ctx.put("start", StartParser.class);
			ctx.put("task", TaskParser.class);
			ctx.put("custom", CustomParser.class);
			ctx.put("decision", DecisionParser.class);
			ctx.put("subprocess", SubProcessParser.class);
			ctx.put("fork", ForkParser.class);
			ctx.put("join", JoinParser.class);
			ctx.put("end", EndParser.class);
			nodeParser = ctx.findByName(nodeName, NodeParser.class);
			nodeParser.parse(element);
			return nodeParser.getModel();
		} catch (RuntimeException e) {
			throw new StudioException(e);
		}
	}

	@Override
	protected boolean validate() {
		return true;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void dispatch(Event evt) {
		if (evt.getName().equals(Bpmdesigner.ON_SAVE)) {
			entity.setContent(designer.getValue().toString());
			setDirty();
		} else {
			Map<String, String> data = (Map<String, String>) evt.getData();
			String type = data.get("type");
			if (type.equals("task"))
				AbstractView.createEntityView(
						new BpmEditorCallback(this, designer, data),
						EntityType.TASK.getName(), null).doOverlapped();
			else if (type.equals("assignee")) {
				BizQueryVo vo = new BizQueryVo();
				vo.setSearchField("userId,name");
				vo.setOrderBy("userId");
				vo.setSql("SELECT userId,name FROM sys_user_info");
				vo.setParams(new FieldVo[0]);
				AbstractView.createQueryView(
						new BpmEditorCallback(this, designer, data), vo, true, evt.getTarget(),
						"ID", Labels.getLabel("entity.name")).doOverlapped();
			} else if (type.equals("assigneeRole")) {
				BizQueryVo vo = new BizQueryVo();
				vo.setSearchField("roleId,name");
				vo.setOrderBy("roleId");
				vo.setSql("SELECT roleId,name FROM sys_role_info");
				vo.setParams(new FieldVo[0]);
				AbstractView.createQueryView(
						new BpmEditorCallback(this, designer, data), vo, true, evt.getTarget(),
						"ID", Labels.getLabel("entity.name")).doOverlapped();
			}
		}
	}
}
